﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using System.Security.Cryptography;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.InteropServices;

namespace NewCROStarter
{
    public partial class Main : Form
    {
        public Main()
        {
            InitializeComponent();
        }

        //config.xml文件位置
        private string confpath;
        //ro配置文件/savedata/OptionInfo.lua位置
        private string roconfpath;
        //ro exe路径
        private string roexepath;
        //ro exe pid
        private int roexepid;
        //ro exe process
        private System.Diagnostics.Process roexeproc;
        //配置：是否启用辅助功能
        private bool conf_EnableTool = true;
        //配置：是否自动关闭辅助工具
        private bool conf_AutoCloseTool = true;
        //配置：是否自动设置ro窗体分辨率
        private bool conf_AutoResizeWin = true;
        //配置：ro窗体分辨率
        private string[] conf_RoWinSize;
        //配置：内测功能激活码
        private string conf_ActivationCode;
        //key
        private string basekey = "57,52,192,25,89,78,210,209,102,252,241,197,39,109,204,25,62,162,10,80,226,144,116,184,235,228,128,149,255,169,24,93,206,199,113,233,41,170,177,200,127,85,49,175,236,79,222,237,204,202,220,146,187,76,251,82,126,51,73,100,145,38,170,35,174,255,35,172,142,80,84,221,192,188,151,88,237,244,244,204,52,249,137,158,118,70,245,144";
        //npcdata1
        List<string[]> npcdata1 = new List<string[]>();
        //npcdata2
        List<string[]> npcdata2 = new List<string[]>();

        private void Form1_Load(object sender, EventArgs e)
        {
            //生成config.xml文件路径
            confpath = System.Threading.Thread.GetDomain().BaseDirectory + @"NewCROStarter\";
            //生成ro配置文件lua路径
            roconfpath = System.Threading.Thread.GetDomain().BaseDirectory + @"savedata\OptionInfo.lua";
            //生成ro exe路径
            roexepath = System.Threading.Thread.GetDomain().BaseDirectory + "ragexe.exe";
            //判断是否已经运行了一个starter进程（是不是双开）、若有，则直接启动ro，不显示辅助工具
            bool IsDoubleOpen = CheckDoubleOpen();
            this.Activate();
            //启动时按下了shift，转去设置页面
            if (Control.ModifierKeys == Keys.Shift)
            {
                Config cfgwindow = new Config();
                DialogResult dr = cfgwindow.ShowDialog();
                //如果窗体返回值是cancel，则退出
                if (dr == DialogResult.Cancel)
                {
                    this.Close();
                    return;
                }
            }
            //读入xml设置
            Load_Config();
            //如果xml不要求启动辅助工具，则启动ro并退出starter
            if ((!conf_EnableTool) || IsDoubleOpen)
            {
                try
                {
                    System.Diagnostics.Process.Start(roexepath, @"1rag1 /account:clientinfo.dat");
                }
                catch (Exception ex)
                { }
                this.Close();
            }
            else 
            {
                try
                {
                    roexeproc = System.Diagnostics.Process.Start(roexepath, @"1rag1 /account:clientinfo.dat");
                    roexepid = roexeproc.Id;
                }
                catch (Exception ex)
                {
                    this.Close();
                }
            }
            //启动roexe监控，如果进程退出则退出辅助工具
            if (conf_AutoCloseTool)
            {
                timer1.Enabled = true;
                timer1.Start();
            }
            //如果xml要求自动排列窗口
            if (conf_AutoResizeWin)
            {
                ResizeRoWindow();
                ResizeToolWindow();
            }
            //取npc数据到内存
            GetNpcData();
        }

        //检测双开
        private bool CheckDoubleOpen()
        {
            System.Diagnostics.Process curproc = System.Diagnostics.Process.GetCurrentProcess();
            System.Diagnostics.Process[] allproc = System.Diagnostics.Process.GetProcessesByName(curproc.ProcessName);
            if (allproc.Length > 1) return true; else return false;
        }

        private void Load_Config()
        {
            //从xml读取
            string starterconfpath = confpath + "Config.xml";
            XmlDocument xd = new XmlDocument();
            xd.Load(starterconfpath);
            XmlNodeList xlist = xd.SelectSingleNode("NewRoStarterConfig").ChildNodes;
            foreach (XmlNode xnode in xlist)
            {
                XmlElement xelement = (XmlElement)xnode;
                if (xelement.GetAttribute("EnableTool") == "1") conf_EnableTool = true; else conf_EnableTool = false;
                if (xelement.GetAttribute("AutoCloseTool") == "1") conf_AutoCloseTool = true; else conf_AutoCloseTool = false;
                if (xelement.GetAttribute("AutoResizeWin") == "1") conf_AutoResizeWin = true; else conf_AutoResizeWin = false;
                conf_RoWinSize = xelement.GetAttribute("RoWinSize").Split('x');
                conf_ActivationCode = xelement.GetAttribute("ActivationCode");
            }
        }

        //自动排列窗口，方法是写savedata/OptionInfo.lua
        private void ResizeRoWindow()
        {
            //以流方式读入lua到array，修改下列文本（ansi格式）
            //OptionInfoList["Window_XPos"] = 0
            //OptionInfoList["Window_YPos"] = 0
            //OptionInfoList["ISFULLSCREENMODE"] = 0
            //OptionInfoList["WIDTH"] = 1280
            //OptionInfoList["HEIGHT"] = 720
            List<string> luaContent = new List<string>();
            using (StreamReader sr = new StreamReader(roconfpath, Encoding.ASCII))
            {
                string line;
                while ((line = sr.ReadLine()) != null)
                {
                    luaContent.Add(line);
                }
            }
            //替换配置文件：以窗口模式启动，ro窗口位于左上角
            for (int i = 0; i < luaContent.Count; i++)
            {
                if (luaContent[i].Contains("Window_XPos"))
                {
                    luaContent[i] = luaContent[i].Remove(luaContent[i].IndexOf('='));
                    luaContent[i] = luaContent[i] + "= 0";
                }
                if (luaContent[i].Contains("Window_YPos"))
                {
                    luaContent[i] = luaContent[i].Remove(luaContent[i].IndexOf('='));
                    luaContent[i] = luaContent[i] + "= 0";
                }
                if (luaContent[i].Contains("ISFULLSCREENMODE"))
                {
                    luaContent[i] = luaContent[i].Remove(luaContent[i].IndexOf('='));
                    luaContent[i] = luaContent[i] + "= 0";
                }
                if (luaContent[i].Contains("WIDTH"))
                {
                    luaContent[i] = luaContent[i].Remove(luaContent[i].IndexOf('='));
                    luaContent[i] = luaContent[i] + "= " + conf_RoWinSize[0];
                }
                if (luaContent[i].Contains("HEIGHT"))
                {
                    luaContent[i] = luaContent[i].Remove(luaContent[i].IndexOf('='));
                    luaContent[i] = luaContent[i] + "= " + conf_RoWinSize[1];
                }
            }
            //写回配置文件
            StreamWriter sw = new StreamWriter(roconfpath, false, Encoding.ASCII);
            foreach (string line in luaContent)
            {
                sw.WriteLine(line);
            }
            sw.Close();
        }

        //根据ro窗口更改工具窗口大小
        private void ResizeToolWindow()
        {
            Rectangle rect = Screen.GetWorkingArea(this);
            int xpos = Int32.Parse(conf_RoWinSize[0]);
            this.Location = new Point(xpos, 0);
            this.Width = rect.Width - xpos;
            this.Height = rect.Height;
        }

        private string DEncryptData(String inName, string keyiv)
        {
            string[] tmpstr = keyiv.Split(',');
            byte[] tdesKey = new byte[24];
            byte[] tdesIV = new byte[8];
            for (int i = 0; i < tmpstr.Length; i++)
            {
                if (i == 16)
                {
                    for (int x = 0; x < 8; x++)
                    {
                        tdesIV[x] = byte.Parse(tmpstr[i]);
                        i++;
                    }
                }
                if (i == 32)
                {
                    for (int y = 0; y < 24; y++)
                    {
                        tdesKey[y] = byte.Parse(tmpstr[i]);
                        i++;
                    }
                }
            }
            //Create the file streams to handle the input and output files.
            FileStream fin = new FileStream(inName, FileMode.Open, FileAccess.Read, FileShare.None, 10240);
            //FileStream fout = new FileStream(outName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, 10240);
            MemoryStream fout = new MemoryStream();
            fout.SetLength(0);
            //Create variables to help with read and write.
            byte[] bin = new byte[1000]; //This is intermediate storage for the encryption.
            long rdlen = 0;              //This is the total number of bytes written.
            long totlen = fin.Length;    //This is the total length of the input file.
            int len;                     //This is the number of bytes to be written at a time.
            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            CryptoStream encStream = new CryptoStream(fout, tdes.CreateDecryptor(tdesKey, tdesIV), CryptoStreamMode.Write);
            //Read from the input file, then encrypt and write to the output file.
            while (rdlen < totlen)
            {
                len = fin.Read(bin, 0, 1000);
                encStream.Write(bin, 0, len);
                rdlen = rdlen + len;
            }
            encStream.Close();
            byte[] b = fout.ToArray();
            string result = Encoding.GetEncoding("GB2312").GetString(b, 0, b.Length);
            fin.Close();
            fout.Close();
            return result;
        }

        private void GetNpcData()
        {
            string tmpstr = DEncryptData(confpath + "npcdata1.dat", basekey);
            char[] splitter = new char[] { '\r', '\n' };
            string[] tmparray1 = tmpstr.Split(splitter, System.StringSplitOptions.RemoveEmptyEntries);
            //airplane	11333	101	814	曹秀儿 半世尔	""	248	65
            char[] splitter2 = new char[] { '\t' };
            foreach (string tmpstr2 in tmparray1)
            {
                string[] tmparray2 = tmpstr2.Split(splitter2, System.StringSplitOptions.RemoveEmptyEntries);
                npcdata1.Add(tmparray2);
            }
            //处理npcdata2
            tmpstr = DEncryptData(confpath + "npcdata2.dat", basekey);
            tmparray1 = tmpstr.Split(splitter, System.StringSplitOptions.RemoveEmptyEntries);
            foreach (string tmpstr2 in tmparray1)
            {
                string[] tmparray2 = tmpstr2.Split(splitter2, System.StringSplitOptions.RemoveEmptyEntries);
                npcdata2.Add(tmparray2);
            }
        }

        private void btn_npcsearch_Click(object sender, EventArgs e)
        {
            lb_searchresult.Items.Clear();
            tb_npclocation.Text = "";
            if (tb_npcsearch.Text == "") return;
            foreach (string[] tmparray1 in npcdata1)
            {
                if (tmparray1[4].Contains(tb_npcsearch.Text))
                {
                    //output:npcname,unknow3+mapname+x+y
                    lb_searchresult.BeginUpdate();
                    lb_searchresult.Items.Add(tmparray1[4] + "," + tmparray1[5] + "," + tmparray1[0] + "," + tmparray1[6] + "," + tmparray1[7]);
                    lb_searchresult.EndUpdate();
                }
                if (tmparray1[5].Contains(tb_npcsearch.Text))
                {
                    //output:npcname,unknow3+mapname+x+y
                    lb_searchresult.BeginUpdate();
                    lb_searchresult.Items.Add(tmparray1[4] + "," + tmparray1[5] + "," + tmparray1[0] + "," + tmparray1[6] + "," + tmparray1[7]);
                    lb_searchresult.EndUpdate();
                }
            }
            foreach (string[] tmparray1 in npcdata2)
            {
                if (tmparray1[4].Contains(tb_npcsearch.Text))
                {
                    //output:npcname,unknow3+mapname+x+y
                    lb_searchresult.BeginUpdate();
                    lb_searchresult.Items.Add(tmparray1[4] + "," + tmparray1[5] + "," + tmparray1[0] + "," + tmparray1[6] + "," + tmparray1[7]);
                    lb_searchresult.EndUpdate();
                }
                if (tmparray1[5].Contains(tb_npcsearch.Text))
                {
                    //output:npcname,unknow3+mapname+x+y
                    lb_searchresult.BeginUpdate();
                    lb_searchresult.Items.Add(tmparray1[4] + "," + tmparray1[5] + "," + tmparray1[0] + "," + tmparray1[6] + "," + tmparray1[7]);
                    lb_searchresult.EndUpdate();
                }
            }
        }

        private void lb_searchresult_SelectedIndexChanged(object sender, EventArgs e)
        {
            string[] tmparray = lb_searchresult.SelectedItem.ToString().Split(new char[] { ',' });
            string tmpstr = @"/navi " + tmparray[tmparray.GetUpperBound(0) - 2] + " " + tmparray[tmparray.GetUpperBound(0) - 1] + @"/" + tmparray[tmparray.GetUpperBound(0)];
            tb_npclocation.Text += tmpstr + "\r\n";
        }

        //启动udp侦听本机1874端口
        private UdpClient udp;
        Thread UDPrecvThread;
        public delegate void processUDPmsg(string udpmsg);
        public processUDPmsg myUDPmsg;
        IPEndPoint RemoteIP;    //可获得消息发送端的ip和端口
        private void btn_StartAIreciver_Click(object sender, EventArgs e)
        {
            RemoteIP = new IPEndPoint(IPAddress.Any, 0);
            udp = new UdpClient(1874);
            myUDPmsg = new processUDPmsg(ProcessMyUDPmsg);
            UDPrecvThread = new Thread(new ThreadStart(recvUDPdata));
            UDPrecvThread.IsBackground = true;
            UDPrecvThread.Start();
            tb_AIoutput.Text = "服务器已启动\r\n";
            btn_StartAIreciver.Enabled = false;
            btn_StopAIreciver.Enabled = true;
        }

        //处理udp消息
        List<string> udpmsgs = new List<string>();
        private void ProcessMyUDPmsg(string msg)
        {
            //输出格式是 x坐标,y坐标,actorID
            //接受到***begin***时，清空数组
            //接受数据存入数组，每次接受到***done***时，排序数组并显示
            //tb_AIoutput.Text += msg + "\r\n";
            if (msg == "***begin***")
            {
                udpmsgs.Clear();
                return;
            }
            if (msg == "***done***")
            {
                ShowUDPmsg();
                return;
            }
            if(msg.StartsWith("***info***"))
            {
                ShowUDPmsg2(msg);
                return;
            }
            udpmsgs.Add(msg);
        }

        private void ShowUDPmsg()
        {
            //输出格式是 x坐标,y坐标,actorID,serial
            tb_AIoutput.Text = "可互动对象已发现：\r\n";
            object[,] actors = new object[udpmsgs.Count, 4];
            for (int i = 0; i < actors.GetUpperBound(0)+1; i++)
            {
                string[] tmpstr = udpmsgs[i].Split(',');
                actors[i, 0] = int.Parse(tmpstr[0]);
                actors[i, 1] = int.Parse(tmpstr[1]);
                actors[i, 2] = int.Parse(tmpstr[2]);
                actors[i, 3] = int.Parse(tmpstr[3]);
            }
            //排序
            ArrayOrder.Orderby(actors, new int[] { 2, 0, 1 }, 0);
            //输出
            for (int i = 0; i < actors.Length; i++)
            {
                tb_AIoutput.Text += "ID:" + actors[i, 2].ToString() + "\t坐标:" + actors[i, 0].ToString() + "x" + actors[i, 1].ToString() + "\t序列号:" + actors[i, 3] + "\r\n";
            }
        }

        private void ShowUDPmsg2(string msg)
        {
            msg = msg.Replace("***info***", "");
            tb_AIoutput2.Text += msg + "\r\n";
        }

        private void recvUDPdata()
        {
            while (true)
            {
                
                try
                {
                    byte[] content = udp.Receive(ref RemoteIP);
                    if (content.Length > 0)
                    {
                        string msg = Encoding.GetEncoding("GB2312").GetString(content);
                        this.Invoke(myUDPmsg, new object[] { msg });
                    }
                }
                catch (Exception ex)
                { 
                    //do nothing
                }
            }
        }

        private void btn_StopAIreciver_Click(object sender, EventArgs e)
        {
            try
            {
                udp.Close();
                UDPrecvThread.Abort();
            }
            catch (Exception ex) { }
            tb_AIoutput.Text += "服务器已停止\r\n";
            btn_StartAIreciver.Enabled = true;
            btn_StopAIreciver.Enabled = false;
        }
        
        //检测ragexe进程是否已退出
        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Stop();
            bool isrorunning = true;
            try
            {
                System.Diagnostics.Process roproc = System.Diagnostics.Process.GetProcessById(roexepid);
                isrorunning = true;
            }
            catch (Exception ex)
            {
                isrorunning = false;
            }
            if (isrorunning) timer1.Start(); else this.Close();
        }

        //设置luasock所需的环境变量
        //LUA_CPATH=d:\lua\?.dll;?.dll
        //LUA_PATH=d:\lua\lua\?.lua;?.lua
        private void btn_setLUAsockEnv_Click(object sender, EventArgs e)
        {
            btn_setLUAsockEnv.Enabled = false;
            string luapath = System.Threading.Thread.GetDomain().BaseDirectory + @"ai\user_ai\lua\lua\?.lua;?.lua";
            string luacpath = System.Threading.Thread.GetDomain().BaseDirectory + @"ai\user_ai\lua\?.dll;?.dll";
            Environment.SetEnvironmentVariable("LUA_PATH", luapath, EnvironmentVariableTarget.User);
            Environment.SetEnvironmentVariable("LUA_CPATH", luacpath, EnvironmentVariableTarget.User);
            btn_setLUAsockEnv.Enabled = true;
        }

        //测试：直接修改ragexe内存实现无限视角等功能，暂不开放
        //private void btn_test_Click(object sender, EventArgs e)
        //{
        //    roexeproc.Refresh();
        //    tb_npclocation.Text += roexeproc.Id.ToString() + " " + roexeproc.Handle.ToString() + " " + roexeproc.MainWindowHandle.ToString() + "\r\n";
        //        int x;
        //        x = WriteROMemory(new IntPtr(0x00721736), new byte[] { 0x64 });
        //        tb_npclocation.Text += x.ToString();
        //        //x = WriteROMemory(new IntPtr(0x008C854A), new byte[] { 0xB5 });
        //        //tb_npclocation.Text += x.ToString();
        //        //x = WriteROMemory(new IntPtr(0x008C854B), new byte[] { 0x44 });
        //        //tb_npclocation.Text += x.ToString();
        //        //x = WriteROMemory(new IntPtr(0x0071AE6D), new byte[] { 0x90 });
        //        //tb_npclocation.Text += x.ToString();
        //        //x = WriteROMemory(new IntPtr(0x0071AE6E), new byte[] { 0x90 });
        //        //tb_npclocation.Text += x.ToString();
        //        //x = WriteROMemory(new IntPtr(0x0071AE6F), new byte[] { 0x90 });
        //        //tb_npclocation.Text += x.ToString();
        //        //x = WriteROMemory(new IntPtr(0x0071AE70), new byte[] { 0x90 });
        //        //tb_npclocation.Text += x.ToString();
        //        //x = WriteROMemory(new IntPtr(0x0071AE71), new byte[] { 0x90 });
        //        //tb_npclocation.Text += x.ToString();
        //        //x = WriteROMemory(new IntPtr(0x0071AE72), new byte[] { 0x90 });
        //        //tb_npclocation.Text += x.ToString();
        //}

        //[DllImport("kernel32.dll", SetLastError = true)]
        //static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten);

        //public int WriteROMemory(IntPtr BaseAddress, byte[] NewVal)
        //{
            //bool ReturnVal;
            //int BytesWritten;
            //ReturnVal = WriteProcessMemory(roexeproc.Handle, BaseAddress, NewVal, (uint)NewVal.Length, out BytesWritten);
            //return BytesWritten;
        //}

        //设置AI攻击策略，通过向lua发送udp消息
        private void btn_AI_Set_DefaultTactics_Click(object sender, EventArgs e)
        {
            //停止\攻击（高）\反制（高）\狙击（高）
            string sendstr="";
            switch (cb_AI_DefaultTactics.Text)
            {
                case "停止":
                    sendstr = "tact_ignore";
                    if (cb_SaveAITact.Checked) SaveTacticsFile("TACT_IGNORE");  //永久设置需要改变H_Tactics.lua
                    break;
                case "攻击（高）":
                    sendstr = "tact_attack_hi";
                    if (cb_SaveAITact.Checked) SaveTacticsFile("TACT_ATTACK_H");  //永久设置需要改变H_Tactics.lua
                    break;
                case "反制（高）":
                    sendstr = "tact_react_hi";
                    if (cb_SaveAITact.Checked) SaveTacticsFile("TACT_REACT_H");  //永久设置需要改变H_Tactics.lua
                    break;
                case "狙击（高）":
                    sendstr = "tact_snipe_hi";
                    if (cb_SaveAITact.Checked) SaveTacticsFile("TACT_SNIPE_H");  //永久设置需要改变H_Tactics.lua
                    break;
            }
            byte[] sendbyte = Encoding.ASCII.GetBytes(sendstr);
            btn_AI_Set_DefaultTactics.Enabled = false;
            try
            {
                udp.Send(sendbyte, sendbyte.Length, RemoteIP);
            }
            catch (Exception ex) { }
            btn_AI_Set_DefaultTactics.Enabled = true;
        }

        private void SaveTacticsFile(string tact)
        {
            string H_Tactics_LUA_path = System.Threading.Thread.GetDomain().BaseDirectory + @"ai\user_ai\H_Tactics.lua";
            List<string> luafilecontent = new List<string>();
            using (StreamReader sr = new StreamReader(H_Tactics_LUA_path, Encoding.UTF8))
            {
                string line;
                while ((line = sr.ReadLine()) != null)
                {
                    if (line.StartsWith("MyTact[0]"))
                    {
                        int startpos = line.IndexOf('{') + 1;
                        int endpos = line.IndexOf(',');
                        string replacestr = line.Substring(startpos, endpos - startpos);
                        string tmpstr = line.Replace(replacestr, tact);
                        luafilecontent.Add(tmpstr);
                    }
                    else
                    {
                        luafilecontent.Add(line);
                    }
                }
            }
            StreamWriter sw = new StreamWriter(H_Tactics_LUA_path, false, Encoding.UTF8, 2048);
            foreach (string writeline in luafilecontent)
            {
                sw.WriteLine(writeline);
            }
            sw.Close();
        }

        private void btn_AI_show_small_window_Click(object sender, EventArgs e)
        {
            try
            {
                AI_small_window aiwin = new AI_small_window(roexeproc, conf_ActivationCode);
                aiwin.Show();
            }
            catch (Exception ex)
            { }
        }

        //显示小地图
        bool showmap = false;
        DXoverlay mapoverlay;
        ROsniffer sniffer;
        List<ROMapText> TextOnMap;
        DisplayType MapDispType = DisplayType.Window_UpperRightCorner;
        float MapZoom = 1.0f;
        private void btn_showmap_Click(object sender, EventArgs e)
        {
            //停止显示地图
            if (showmap && combo_map_disp_type.SelectedItem.ToString() == "不显示")
            {
                showmap = false;
                try
                {
                    sniffer.Stop();
                    mapoverlay.RenderROMap("1x1", roexeproc, CharPosX, CharPosY, TextOnMap, MapDispType, MapZoom, "Form1");
                }
                catch
                { }
                return;
            }
            //未显示但需要首次显示
            if (showmap == false && combo_map_disp_type.SelectedItem.ToString() != "不显示")
            {
                //init overlay form
                mapoverlay = new DXoverlay();
                mapoverlay.Show();
                mapoverlay.InitRenderROMap(Application.StartupPath + @"\NewCROStarter\Maps");
                //init network sniffer
                sniffer = new ROsniffer(roexepid);
                ROsniffer.MapChanged += new ROsniffer.MapChangedEventHandler(ROMapChangedEvent);
                ROsniffer.CharacterMoves += new ROsniffer.CharacterMovesEventHandler(ROCharMovedEvent);
                sniffer.Start();
                //init map text（临时，因为当前为完成在地图上显示文本的功能）
                TextOnMap = new List<ROMapText>();
                ROMapText tmptext = new ROMapText(60, 210, new System.Drawing.Font("宋体", 18f), Color.White, "test123");
                TextOnMap.Add(tmptext);

                showmap = true;
            }
            //已显示但需要切换显示效果
            if(showmap && combo_map_disp_type.SelectedItem.ToString() != "不显示")
            {
                //set MapDispType
                if (combo_map_disp_type.SelectedItem.ToString() == "以角色为中心")
                { MapDispType = DisplayType.App_CharacterCenter; }
                else if (combo_map_disp_type.SelectedItem.ToString() == "在窗口右上角")
                { MapDispType = DisplayType.App_UpperRightCorner; }
                else if (combo_map_disp_type.SelectedItem.ToString() == "在屏幕右上角")
                { MapDispType = DisplayType.Window_UpperRightCorner; }
                //set other map display parameters
                MapZoom = float.Parse(tb_map_zoom.Text);

                showmap = true;
            }
        }

        //接收角色移动事件
        int CharPosX, CharPosY;
        private void ROCharMovedEvent(object sender, CharacterMovesEventArgs ev)
        {
            SetText(ev.CharacterTo_X.ToString() + "x" + ev.CharacterTo_Y.ToString() + "\r\n");
            CharPosX = ev.CharacterTo_X;
            CharPosY = ev.CharacterTo_Y;
            if (ismapserver_detected)
                mapoverlay.RenderROMap(MapName, roexeproc, CharPosX, CharPosY, TextOnMap, MapDispType, MapZoom, "Form1");
        }

        //接收地图切换事件
        string MapName;
        bool ismapserver_detected = false;
        private void ROMapChangedEvent(object sender, MapChangedEventArgs ev)
        {
            SetText(ev.MapName + " " + ev.MapX.ToString() + "x" + ev.MapY.ToString() + "\r\n");
            MapName = ev.MapName;
            CharPosX = ev.MapX;
            CharPosY = ev.MapY;
            ismapserver_detected = true;
            //只有首次检测到地图切换信息时，才开始绘图
            //while (showmap)
            //{
                mapoverlay.RenderROMap(MapName, roexeproc, CharPosX, CharPosY, TextOnMap, MapDispType, MapZoom, "Form1");
                //Application.DoEvents();
            //}
        }

        //debug use
        private delegate void SetTextCallback(string text);
        private void SetText(string txt)
        {
            if (textBox1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { txt });
            }
            else
            {
                textBox1.Text += txt;
            }
        }
    }
}
